/**
  ******************************************************************************
    @file    usbd_core.c
    @author  MCD Application Team
    @version V1.1.0
    @date    19-March-2012
    @brief   This file provides all the USBD core functions.
  ******************************************************************************
    @attention

    <h2><center>&copy; COPYRIGHT 2012 STMicroelectronics</center></h2>

    Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
    You may not use this file except in compliance with the License.
    You may obtain a copy of the License at:

           http://www.st.com/software_license_agreement_liberty_v2

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

  ******************************************************************************
*/

/* Includes ------------------------------------------------------------------*/
#include "usbd_core.h"
#include "usbd_req.h"
#include "usbd_ioreq.h"
#include "usb_dcd_int.h"
#include "usb_bsp.h"

/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
    @{
*/


/** @defgroup USBD_CORE
    @brief usbd core module
    @{
*/

/** @defgroup USBD_CORE_Private_TypesDefinitions
    @{
*/
/**
    @}
*/


/** @defgroup USBD_CORE_Private_Defines
    @{
*/

/**
    @}
*/


/** @defgroup USBD_CORE_Private_Macros
    @{
*/
/**
    @}
*/




/** @defgroup USBD_CORE_Private_FunctionPrototypes
    @{
*/
static uint8_t USBD_SetupStage(USB_OTG_CORE_HANDLE *pdev);
static uint8_t USBD_DataOutStage(USB_OTG_CORE_HANDLE *pdev, uint8_t epnum);
static uint8_t USBD_DataInStage(USB_OTG_CORE_HANDLE *pdev, uint8_t epnum);
static uint8_t USBD_SOF(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t USBD_Reset(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t USBD_Suspend(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t USBD_Resume(USB_OTG_CORE_HANDLE  *pdev);
#ifdef VBUS_SENSING_ENABLED
static uint8_t USBD_DevConnected(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t USBD_DevDisconnected(USB_OTG_CORE_HANDLE  *pdev);
#endif
static uint8_t USBD_IsoINIncomplete(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t USBD_IsoOUTIncomplete(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t  USBD_RunTestMode (USB_OTG_CORE_HANDLE  *pdev) ;
/**
    @}
*/

/** @defgroup USBD_CORE_Private_Variables
    @{
*/

__IO USB_OTG_DCTL_TypeDef SET_TEST_MODE;

USBD_DCD_INT_cb_TypeDef USBD_DCD_INT_cb =
{
	USBD_DataOutStage,
	USBD_DataInStage,
	USBD_SetupStage,
	USBD_SOF,
	USBD_Reset,
	USBD_Suspend,
	USBD_Resume,
	USBD_IsoINIncomplete,
	USBD_IsoOUTIncomplete,
#ifdef VBUS_SENSING_ENABLED
	USBD_DevConnected,
	USBD_DevDisconnected,
#endif
};

USBD_DCD_INT_cb_TypeDef  *USBD_DCD_INT_fops = &USBD_DCD_INT_cb;
/**
    @}
*/

/** @defgroup USBD_CORE_Private_Functions
    @{
*/

/**
    @brief  USBD_Init
          Initailizes the device stack and load the class driver
    @param  pdev: device instance
    @param  core_address: USB OTG core ID
    @param  class_cb: Class callback structure address
    @param  usr_cb: User callback structure address
    @retval None
*/
void USBD_Init(USB_OTG_CORE_HANDLE *pdev,
               USB_OTG_CORE_ID_TypeDef coreID,
               USBD_DEVICE *pDevice,
               USBD_Class_cb_TypeDef *class_cb,
               USBD_Usr_cb_TypeDef *usr_cb)
{
	/* Hardware Init */
	USB_OTG_BSP_Init(pdev);
	USBD_DeInit(pdev);
	/*Register class and user callbacks */
	pdev->dev.class_cb = class_cb;
	pdev->dev.usr_cb = usr_cb;
	pdev->dev.usr_device = pDevice;
	/* set USB OTG core params */
	DCD_Init(pdev, coreID);
	/* Upon Init call usr callback */
	pdev->dev.usr_cb->Init();
	/* Enable Interrupts */
	USB_OTG_BSP_EnableInterrupt(pdev);
}

/**
    @brief  USBD_DeInit
          Re-Initialize th device library
    @param  pdev: device instance
    @retval status: status
*/
USBD_Status USBD_DeInit(USB_OTG_CORE_HANDLE *pdev)
{
	/* Software Init */
	return USBD_OK;
}

/**
    @brief  USBD_SetupStage
          Handle the setup stage
    @param  pdev: device instance
    @retval status
*/
static uint8_t USBD_SetupStage(USB_OTG_CORE_HANDLE *pdev)
{
	USB_SETUP_REQ req;
	USBD_ParseSetupRequest(pdev, &req);
	switch (req.bmRequest & 0x1F)
	{
		case USB_REQ_RECIPIENT_DEVICE:
			USBD_StdDevReq (pdev, &req);
			break;
		case USB_REQ_RECIPIENT_INTERFACE:
			USBD_StdItfReq(pdev, &req);
			break;
		case USB_REQ_RECIPIENT_ENDPOINT:
			USBD_StdEPReq(pdev, &req);
			break;
		default:
			DCD_EP_Stall(pdev, req.bmRequest & 0x80);
			break;
	}
	return USBD_OK;
}

/**
    @brief  USBD_DataOutStage
          Handle data out stage
    @param  pdev: device instance
    @param  epnum: endpoint index
    @retval status
*/
static uint8_t USBD_DataOutStage(USB_OTG_CORE_HANDLE *pdev, uint8_t epnum)
{
	USB_OTG_EP *ep;
	if(epnum == 0)
	{
		ep = &pdev->dev.out_ep[0];
		if ( pdev->dev.device_state == USB_OTG_EP0_DATA_OUT)
		{
			if(ep->rem_data_len > ep->maxpacket)
			{
				ep->rem_data_len -=  ep->maxpacket;
				if(pdev->cfg.dma_enable == 1)
				{
					/* in slave mode this, is handled by the RxSTSQLvl ISR */
					ep->xfer_buff += ep->maxpacket;
				}
				USBD_CtlContinueRx (pdev,
				                    ep->xfer_buff,
				                    MIN(ep->rem_data_len,ep->maxpacket));
			}
			else
			{
				if((pdev->dev.class_cb->EP0_RxReady != NULL)&&
				        (pdev->dev.device_status == USB_OTG_CONFIGURED))
					pdev->dev.class_cb->EP0_RxReady(pdev);
				USBD_CtlSendStatus(pdev);
			}
		}
	}
	else if((pdev->dev.class_cb->DataOut != NULL)&&
	        (pdev->dev.device_status == USB_OTG_CONFIGURED))
		pdev->dev.class_cb->DataOut(pdev, epnum);
	return USBD_OK;
}

/**
    @brief  USBD_DataInStage
          Handle data in stage
    @param  pdev: device instance
    @param  epnum: endpoint index
    @retval status
*/
static uint8_t USBD_DataInStage(USB_OTG_CORE_HANDLE *pdev, uint8_t epnum)
{
	USB_OTG_EP *ep;
	if(epnum == 0)
	{
		ep = &pdev->dev.in_ep[0];
		if ( pdev->dev.device_state == USB_OTG_EP0_DATA_IN)
		{
			if(ep->rem_data_len > ep->maxpacket)
			{
				ep->rem_data_len -=  ep->maxpacket;
				if(pdev->cfg.dma_enable == 1)
				{
					/* in slave mode this, is handled by the TxFifoEmpty ISR */
					ep->xfer_buff += ep->maxpacket;
				}
				USBD_CtlContinueSendData (pdev,
				                          ep->xfer_buff,
				                          ep->rem_data_len);
			}
			else
			{
				/* last packet is MPS multiple, so send ZLP packet */
				if((ep->total_data_len % ep->maxpacket == 0) &&
				        (ep->total_data_len >= ep->maxpacket) &&
				        (ep->total_data_len < ep->ctl_data_len ))
				{
					USBD_CtlContinueSendData(pdev, NULL, 0);
					ep->ctl_data_len = 0;
				}
				else
				{
					if((pdev->dev.class_cb->EP0_TxSent != NULL)&&
					        (pdev->dev.device_status == USB_OTG_CONFIGURED))
						pdev->dev.class_cb->EP0_TxSent(pdev);
					USBD_CtlReceiveStatus(pdev);
				}
			}
		}
		if (pdev->dev.test_mode == 1)
		{
			USBD_RunTestMode(pdev);
			pdev->dev.test_mode = 0;
		}
	}
	else if((pdev->dev.class_cb->DataIn != NULL)&&
	        (pdev->dev.device_status == USB_OTG_CONFIGURED))
		pdev->dev.class_cb->DataIn(pdev, epnum);
	return USBD_OK;
}




/**
    @brief  USBD_RunTestMode
          Launch test mode process
    @param  pdev: device instance
    @retval status
*/
static uint8_t  USBD_RunTestMode (USB_OTG_CORE_HANDLE  *pdev)
{
	USB_OTG_WRITE_REG32(&pdev->regs.DREGS->DCTL, SET_TEST_MODE.d32);
	return USBD_OK;
}

/**
    @brief  USBD_Reset
          Handle Reset event
    @param  pdev: device instance
    @retval status
*/

static uint8_t USBD_Reset(USB_OTG_CORE_HANDLE  *pdev)
{
	/* Open EP0 OUT */
	DCD_EP_Open(pdev,
	            0x00,
	            USB_OTG_MAX_EP0_SIZE,
	            EP_TYPE_CTRL);
	/* Open EP0 IN */
	DCD_EP_Open(pdev,
	            0x80,
	            USB_OTG_MAX_EP0_SIZE,
	            EP_TYPE_CTRL);
	/* Upon Reset call usr call back */
	pdev->dev.device_status = USB_OTG_DEFAULT;
	pdev->dev.usr_cb->DeviceReset(pdev->cfg.speed);
	return USBD_OK;
}

/**
    @brief  USBD_Resume
          Handle Resume event
    @param  pdev: device instance
    @retval status
*/

static uint8_t USBD_Resume(USB_OTG_CORE_HANDLE  *pdev)
{
	/* Upon Resume call usr call back */
	pdev->dev.usr_cb->DeviceResumed();
	pdev->dev.device_status = pdev->dev.device_old_status;
	pdev->dev.device_status = USB_OTG_CONFIGURED;
	return USBD_OK;
}


/**
    @brief  USBD_Suspend
          Handle Suspend event
    @param  pdev: device instance
    @retval status
*/

static uint8_t USBD_Suspend(USB_OTG_CORE_HANDLE  *pdev)
{
	pdev->dev.device_old_status = pdev->dev.device_status;
	pdev->dev.device_status  = USB_OTG_SUSPENDED;
	/* Upon Resume call usr call back */
	pdev->dev.usr_cb->DeviceSuspended();
	return USBD_OK;
}


/**
    @brief  USBD_SOF
          Handle SOF event
    @param  pdev: device instance
    @retval status
*/

static uint8_t USBD_SOF(USB_OTG_CORE_HANDLE  *pdev)
{
	if(pdev->dev.class_cb->SOF)
		pdev->dev.class_cb->SOF(pdev);
	return USBD_OK;
}
/**
    @brief  USBD_SetCfg
         Configure device and start the interface
    @param  pdev: device instance
    @param  cfgidx: configuration index
    @retval status
*/

USBD_Status USBD_SetCfg(USB_OTG_CORE_HANDLE  *pdev, uint8_t cfgidx)
{
	pdev->dev.class_cb->Init(pdev, cfgidx);
	/* Upon set config call usr call back */
	pdev->dev.usr_cb->DeviceConfigured();
	return USBD_OK;
}

/**
    @brief  USBD_ClrCfg
          Clear current configuration
    @param  pdev: device instance
    @param  cfgidx: configuration index
    @retval status: USBD_Status
*/
USBD_Status USBD_ClrCfg(USB_OTG_CORE_HANDLE  *pdev, uint8_t cfgidx)
{
	pdev->dev.class_cb->DeInit(pdev, cfgidx);
	return USBD_OK;
}

/**
    @brief  USBD_IsoINIncomplete
          Handle iso in incomplete event
    @param  pdev: device instance
    @retval status
*/
static uint8_t USBD_IsoINIncomplete(USB_OTG_CORE_HANDLE  *pdev)
{
	pdev->dev.class_cb->IsoINIncomplete(pdev);
	return USBD_OK;
}

/**
    @brief  USBD_IsoOUTIncomplete
          Handle iso out incomplete event
    @param  pdev: device instance
    @retval status
*/
static uint8_t USBD_IsoOUTIncomplete(USB_OTG_CORE_HANDLE  *pdev)
{
	pdev->dev.class_cb->IsoOUTIncomplete(pdev);
	return USBD_OK;
}

#ifdef VBUS_SENSING_ENABLED
/**
    @brief  USBD_DevConnected
          Handle device connection event
    @param  pdev: device instance
    @retval status
*/
static uint8_t USBD_DevConnected(USB_OTG_CORE_HANDLE  *pdev)
{
	pdev->dev.usr_cb->DeviceConnected();
	pdev->dev.connection_status = 1;
	return USBD_OK;
}

/**
    @brief  USBD_DevDisconnected
          Handle device disconnection event
    @param  pdev: device instance
    @retval status
*/
static uint8_t USBD_DevDisconnected(USB_OTG_CORE_HANDLE  *pdev)
{
	pdev->dev.usr_cb->DeviceDisconnected();
	pdev->dev.class_cb->DeInit(pdev, 0);
	pdev->dev.connection_status = 0;
	return USBD_OK;
}
#endif
/**
    @}
*/


/**
    @}
*/


/**
    @}
*/

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

